package cn.org.rookie.mybatismapper.sql;

import cn.org.rookie.mybatismapper.entiey.Column;
import cn.org.rookie.mybatismapper.entiey.Table;
import org.apache.ibatis.jdbc.SQL;

import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;

public class SqlBuilder {

    private final SQL insert = new SQL();
    private final SQL delete = new SQL();
    private final SQL update = new SQL();
    private final SQL select = new SQL();
    private final Table table;
    private String sql;

    public SqlBuilder(Table table) {
        this.table = table;
    }

    private static String ifScript(String sql, String field) {
        return String.format("<if test=\"%s != null\">%s</if>", field, sql);
    }

    private static String sharp(String field) {
        return String.format("#{%s}", field);
    }

    private static String join(Collection<String> source) {
        StringJoiner joiner = new StringJoiner(",", " ORDER BY ", "");
        for (String s : source) {
            joiner.add(s);
        }
        return joiner.toString();
    }

    public SqlBuilder insert() {
        insert.INSERT_INTO(table.getName());
        for (Column column : table.getColumns()) {
            insert.VALUES(column.getName(), sharp(column.getFieldName()));
        }
        sql = insert.toString().replaceAll("\n", " ");
        return this;
    }

    public SqlBuilder delete() {
        delete.DELETE_FROM(table.getName());
        sql = delete.toString().replaceAll("\n", " ");
        return this;
    }

    public SqlBuilder update() {
        StringBuilder sets = new StringBuilder();
        update.UPDATE(table.getName());
        for (int i = 0; i < table.getColumns().size(); i++) {
            Column column = table.getColumns().get(i);
            if (!column.getName().equals(table.getPrimaryKey().getName())) {
                String name = "param." + column.getFieldName();
                if (i == table.getColumns().size() - 1) {
                    sets.append(ifScript(column.getName() + " = " + sharp(name), name));
                } else {
                    sets.append(ifScript(column.getName() + " = " + sharp(name) + ",", name));
                }
            }
        }
        update.SET(sets.toString());
        sql = update.toString().replaceAll("\n", " ");
        return this;
    }

    public SqlBuilder select() {
        List<Column> columns = table.getColumns();
        String[] array = columns.stream().map(Column::getName).toArray(n -> new String[columns.size()]);
        select.SELECT(array).FROM(table.getName());
        sql = select.toString().replaceAll("\n", " ");
        return this;
    }

    public SqlBuilder byPrimaryKey() {
        where();
        eq(table.getPrimaryKey().getName(), "param." + table.getPrimaryKey().getName());
        return this;
    }

    public SqlBuilder where(Wrapper wrapper) {
        StringBuilder where = new StringBuilder();
        if (wrapper != null) {
            List<Condition> conditions = wrapper.getConditions();
            for (int i = 0; i < conditions.size(); i++) {
                Condition condition = conditions.get(i);
                if (i != 0) {
                    if (condition.isAnd()) {
                        where.append(" AND ");
                    } else {
                        where.append(" OR ");
                    }
                }
                where.append(condition.render());
            }
            List<String> order = wrapper.getOrder();
            if (!where.isEmpty()) {
                if (sql.contains("WHERE")) {
                    sql += (" AND " + where);
                } else {
                    sql += (" WHERE " + where);
                }
            }

            if (!order.isEmpty()) {
                sql += join(order);
            }
        }
        return this;
    }

    public String build() {
        return String.format("<script>\n%s\n</script>", sql);
    }

    private void and() {
        sql += " AND ";
    }

    private void or() {
        sql += " OR ";
    }

    private void where() {
        sql += " WHERE ";
    }

    private void eq(String columnName, String fieldName) {
        sql += String.format("%s = %s", columnName, sharp(fieldName));
    }

    private void gt(String columnName, String fieldName) {
        sql += String.format("%s &gt; %s", columnName, sharp(fieldName));
    }

    private void lt(String columnName, String fieldName) {
        sql += String.format("%s &gt; %s", columnName, sharp(fieldName));
    }

    private void like(String columnName, String fieldName) {
        sql += " " + columnName + " LIKE concat(concat('%', " + sharp(fieldName) + "), '%')";
    }

    private void isNull(String columnName) {
        sql += String.format("%s IS NULL", columnName);
    }

    private void isNotNull(String columnName) {
        sql += String.format("%s IS NOT NULL", columnName);
    }
}
